package com.fanrui.code.mahjong.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.fanrui.code.chess.IChessCard;
import com.fanrui.code.chess.mahjong.BaoHuRules;
import com.fanrui.code.chess.mahjong.Mahjong;
import com.fanrui.code.chess.mahjong.MahjongGamer;
import com.fanrui.code.chess.mahjong.MahjongRules;
import com.fanrui.code.util.core.ListUtil;
import com.fanrui.code.util.logger.Stdout;
import com.fanrui.proto.cmd.HuType;
import com.fanrui.proto.cmd.MahjongGroup;
import com.fanrui.proto.cmd.MahjongGroup.GroupType;
import com.fanrui.proto.cmd.MahjongType;
import com.fanrui.proto.cmd.Operation;
import com.fanrui.proto.cmd.WannengType;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;

public class MahjongCardUtil {

	// singleton
	private MahjongCardUtil() {
	}

	private static class MahjongCardUtilSingleton {
		private static MahjongCardUtil instance = new MahjongCardUtil();
	}

	public static MahjongCardUtil getInstance() {
		return MahjongCardUtilSingleton.instance;
	}

	public static final Map<Integer, Mahjong> cards = new HashMap<Integer, Mahjong>();

	public static JsonArray modelCards;

	public static void init() {
		int id = 1;
		for (MahjongType type : MahjongType.values()) {
			for (int i = 1; i <= (type == MahjongType.SAN ? 7 : 9); i++) {
				for (int j = 0; j <= 3; j++) {
					cards.put(id, new Mahjong(id, i, type, null));
					id++;
				}
			}
		}
	}

	public static Mahjong getMahjong(int id) {
		return cards.get(id);
	}

	public static Mahjong getMahjong(int num, MahjongType type) {
		for (Mahjong mahjong : cards.values()) {
			if (mahjong.getNumber() == num && mahjong.getType() == type) {
				return mahjong;
			}
		}
		return null;
	}

	/**
	 * 根据模板创建麻将
	 */
	public List<Mahjong> createModelCard(MahjongRules gameRules, List<Mahjong> normalCards) {
		if (normalCards == null) {
			normalCards = createMahjongCard(gameRules, true);// 标准的麻将
		}
		// 打乱
		Collections.shuffle(normalCards);
		Mahjong[][] modelsArrays = new Mahjong[5][14];
		if (modelCards != null && modelCards.size() > 0) {
			for (int i = 0; i < modelCards.size(); i++) {
				JsonElement elemnt = modelCards.get(i);
				if (elemnt != null && elemnt.isJsonArray()) {
					JsonArray seatCards = modelCards.get(i).getAsJsonArray();
					for (int j = 0; j < seatCards.size(); j++) {
						Integer findId = seatCards.get(j).getAsInt();
						Mahjong modleCard = null;
						if (findId != null) {
							Iterator<Mahjong> iterator = normalCards.iterator();
							while (iterator.hasNext()) {
								Mahjong findCard = iterator.next();
								if (findCard.getId() == findId) {
									iterator.remove();
									modleCard = findCard;
									break;
								}
							}
						}
						if (modleCard == null) {
							modleCard = normalCards.remove(0);
						}
						modelsArrays[i][j] = modleCard;
					}
				}
			}
		}
		List<Mahjong> result = new ArrayList<Mahjong>();
		for (int i = 0; i < modelsArrays.length; i++) {
			for (int j = 0; j < modelsArrays[i].length; j++) {
				if (i != 0 && j == 13) {
					continue;
				}
				Mahjong modleCard = modelsArrays[i][j];
				if (modleCard == null) {
					modleCard = normalCards.remove(0);
				}
				result.add(modleCard);
			}
		}
		result.addAll(normalCards);
		return result;
	}

	/**
	 * 根据麻将规则创建麻将
	 * 
	 * @param gameRules
	 * @param useNew
	 * @return
	 */
	public List<Mahjong> createMahjongCard(MahjongRules gameRules, boolean useNew) {
		List<Mahjong> list = new ArrayList<Mahjong>();
		int id = 1;
		if(gameRules instanceof BaoHuRules) {
			id = 37;
		}
		if (gameRules.getCards() != null) {
			Iterator<Entry<MahjongType, Integer>> iterator = gameRules.getCards().entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<MahjongType, Integer> entry = iterator.next();
				for (int i = 1; i < entry.getValue(); i++) {
					for (int j = 0; j <= 3; j++) {
						Mahjong card;
						if (useNew) {
						card = new Mahjong(id, i, entry.getKey(), null);
						} else {
							card = MahjongCardUtil.getMahjong(id);
						}
						list.add(card);
						id++;
					}
				}
			}
		}
		return list;
	}

	/**
	 * 是否可以胡牌
	 * 
	 * @param card
	 * @param gamer
	 * @return boolean
	 */
	public boolean checkHu(Mahjong card, MahjongGamer gamer, MahjongRules gameRules) {
		List<Mahjong> hands = MahjongCardUtil.getInstance().clone(gamer.getHandCards());
		if (card != null && hands.size() % 3 == 1 && card.getNumber() > 0) {
			hands.add(card);
		}
		return checkHu(hands, true, gameRules);
	}

	public boolean checkHu(Collection<Mahjong> hands, boolean isRemoveLaizi, MahjongRules gameRules) {
		List<Mahjong> newhands = MahjongCardUtil.getInstance().clone(hands);
		if (newhands.size() % 3 != 2) {
			return false;
		}
		// 上精当癞子
		int guiNumber = 0;
		int laizi = 0;
		if(isRemoveLaizi) {
			Iterator<Mahjong> iterator = newhands.iterator();
			while (iterator.hasNext()) {
				Mahjong mj = iterator.next();
				if (mj.getWannengType() != null) {
					if (mj.getWannengType() == WannengType.ShangZheng || mj.getWannengType() == WannengType.ShangFu || mj.getWannengType() == WannengType.Gui) {
						if(mj.getWannengType() == WannengType.Gui) {
							guiNumber++;
						}
						laizi++;
						iterator.remove();
					}
				}
			}
		}
		 //特殊牌型（赣州：七对，十三烂；爆胡：七对，四鬼胡）
		if (checkQiDui(newhands, laizi)) {
			 return true;
		 }
		if(gameRules instanceof BaoHuRules) {
			if(guiNumber == 4) {
				return true;
			}
		}
		if(gameRules instanceof BaoHuRules) {
			 if (checkShiSanLan(newhands, laizi)) {
				 return true;
			 }
		}
		List<Integer> list = changeToIdMahjong(newhands);
		return checkHu(list, laizi);
	}

	/**
	 * 检查手牌加癞子是否全部成扑（顺子或碰子）
	 * 
	 * @param cards
	 *            手牌
	 * @param laizi
	 *            癞子个数
	 * @return 
	 */
	private boolean isPu(List<Integer> cards, int laizi) {
		if (cards.size() == 0) {
			return true;
		}
		// 若第一张是顺子中的一张
		for (int first = cards.get(0) - 2; first <= cards.get(0); first++) {
			if (first % 10 > 7 || (laizi == 0 && first < cards.get(0))) {
				// 剪枝：顺子第一张牌不会大于7点、无赖子情况下顺子第一张只能用手上的牌
				continue;
			}
			int shunCount = 0;
			for (int i = 0; i < 3; i++) {
				if (ListUtil.indexOf(cards, first+i) >= 0) {
					shunCount++;
				}
			}
			if (shunCount == 3 || shunCount + laizi >= 3) {
				// 找到包含第一张牌的顺子
				List<Integer> puCards = new ArrayList<Integer>(cards);
				int puLaizi = laizi;
				for (int i = 0; i < 3; i++) {
					int deletePos = ListUtil.indexOf(puCards, first+i);
					if (deletePos >= 0) {
						puCards.remove(deletePos);
					} else {
						puLaizi--;
					}
				}
				if (isPu(puCards, puLaizi)) {
					// 剩下的牌成扑
					return true;
				}
			}
		}
		// 若第一张是刻子中的一张
		int keziCount = 1;
		int keziCard = cards.get(0);
		if (cards.size() > 1 && cards.get(1) == keziCard) {
			keziCount++;
		}
		if (cards.size() > 2 && cards.get(2) == keziCard) {
			keziCount++;
		}
		if (keziCount == 3 || keziCount + laizi >= 3) {
			List<Integer> puCards = new ArrayList<Integer>(cards);
			int puLaizi = laizi;
			for (int i = 0; i < 3; i++) {
				int deletePos = ListUtil.indexOf(puCards, keziCard);
				if (deletePos >= 0) {
					puCards.remove(deletePos);
				} else {
					puLaizi--;
				}
			}
			if (isPu(puCards, puLaizi)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 检查手牌加癞子是否全部是杠子或碰子
	 * 
	 * @param cards
	 *            手牌
	 * @param laizi
	 *            癞子个数
	 * @return 
	 */
	private boolean isDaDui(List<Integer> cards, int laizi) {
		if (cards.size() == 0) {
			return true;
		}
		// 若第一张是刻子中的一张
		int keziCount = 1;
		int keziCard = cards.get(0);
		if (cards.size() > 1 && cards.get(1) == keziCard) {
			keziCount++;
		}
		if (cards.size() > 2 && cards.get(2) == keziCard) {
			keziCount++;
		}
		if (keziCount == 3 || keziCount + laizi >= 3) {
			List<Integer> puCards = new ArrayList<Integer>(cards);
			int puLaizi = laizi;
			for (int i = 0; i < 3; i++) {
				int deletePos = ListUtil.indexOf(puCards, keziCard);
				if (deletePos >= 0) {
					puCards.remove(deletePos);
				} else {
					puLaizi--;
				}
			}
			if (isDaDui(puCards, puLaizi)) {
				return true;
			}
		}
		// 若第一张是杠子中的一张
		int gangziCount = 1;
		int gangziCard = cards.get(0);
		if (cards.size() > 1 && cards.get(1) == gangziCard) {
			gangziCount++;
		}
		if (cards.size() > 2 && cards.get(2) == gangziCard) {
			gangziCount++;
		}
		if (cards.size() > 3 && cards.get(3) == gangziCard) {
			gangziCount++;
		}
		if (gangziCount == 4 || gangziCount + laizi >= 4) {
			List<Integer> puCards = new ArrayList<Integer>(cards);
			int puLaizi = laizi;
			for (int i = 0; i < 4; i++) {
				int deletePos = ListUtil.indexOf(puCards, gangziCard);
				if (deletePos >= 0) {
					puCards.remove(deletePos);
				} else {
					puLaizi--;
				}
			}
			if (isDaDui(puCards, puLaizi)) {
				return true;
			}
		}
		return false;
	}

	private boolean checkHu(List<Integer> cards, int laizi) {
		if ((cards.size() + laizi + 1) % 3 != 0) {
			// 若牌张数不是2、5、8、11、14则不能胡
			return false;
		}
		// 排序方便胡牌判断
		Collections.sort(cards);

		// 依次删除一对牌做将，其余牌全部成扑则可胡
		for (int i = 0; i < cards.size(); i++) {
			if (i > 0 && cards.get(i) == cards.get(i - 1)) {
				// 和上一次是同样的牌，避免重复计算
				continue;
			}
			if ((i + 1 < cards.size() && cards.get(i) == cards.get(i + 1)) || laizi > 0) {
				// 找到对子、或是用一张癞子拼出的对子
				List<Integer> puCards = new ArrayList<Integer>(cards);
				int puLaizi = laizi;
				puCards.remove(i);
				if (puCards.size() > i && puCards.get(i) == cards.get(i)) {
					puCards.remove(i);
				} else {
					puLaizi--;
				}
				// 删去对子判断剩下的牌是否成扑
				if (isPu(puCards, puLaizi)) {
					return true;
				}
			}
		}
		if (laizi >= 2 && isPu(cards, laizi - 2)) {
			// 两个癞子做将牌
			return true;
		}
		return false;
	}

	@SuppressWarnings("unused")
	private boolean checkHu(Map<MahjongType, List<Mahjong>> groups) {
		for (List<Mahjong> list : groups.values()) {// 牌数量不能构成胡牌的规则
			if (list.size() % 3 == 1) {
				return false;
			}
		}
		boolean haveJiang = false;
		for (List<Mahjong> list : groups.values()) {
			if (list.size() % 3 == 0) {
				if (!checkValidHu(list)) {
					return false;
				}
			}
			if (list.size() % 3 == 2) {
				if (!haveJiang) {
					haveJiang = true;
				} else {
					return false;
				}
				if (!checkHaveJiangValidHu(list)) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * 检测一副牌是都为顺子或者刻子
	 * 
	 * @param val
	 * @return boolean
	 */
	private boolean checkValidHu(Collection<Mahjong> val) {
		if (val == null || val.isEmpty()) { // 空牌都是合法的
			return true;
		}
		List<Mahjong> cards = MahjongCardUtil.getInstance().clone(val);
		Collections.sort(cards);
		Mahjong startCard = cards.get(0);
		boolean canContinue = true;
		while (!cards.isEmpty() && canContinue) {
			if (removeCard(cards, startCard, 3, true).isEmpty()) {
				if (startCard.getType() == MahjongType.SAN) {// 如果是散牌
																// 没有三个的直接返回false
					return false;
				} else {
					canContinue = !removeSuccession(cards, startCard).isEmpty();
				}

			}
			if (!cards.isEmpty()) {
				startCard = cards.get(0);
			}

		}
		return canContinue;
	}

	/**
	 * 检测一副含有将牌 的牌集合中都为顺子或者刻子
	 * 
	 * @param val
	 * @return boolean
	 */
	private boolean checkHaveJiangValidHu(Collection<Mahjong> val) {
		for (Mahjong card : val) {
			if (getCardNumFromHand(card, val) >= 2) {
				List<Mahjong> cards = MahjongCardUtil.getInstance().clone(val);
				removeCard(cards, card, 2, true);
				if (checkValidHu(cards)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 删除连续的3张牌 如果没有连续的三张牌则不删除
	 * 
	 * @param cards
	 * @param card
	 * @return Collection<Mahjong>
	 */
	public Collection<Mahjong> removeSuccession(Collection<Mahjong> cards, Mahjong card) {
		if (card.getNumber() > 7 || card.getType() == MahjongType.SAN) {// 如果点数是7
																		// 或者是散牌肯定没有顺子
			return Collections.emptyList();
		}
		if (getCardNumFromHand(card, cards) < 1 || getCardNumFromHand(card.getNext(), cards) < 1
				|| getCardNumFromHand(card.getNext().getNext(), cards) < 1) {
			return Collections.emptyList();
		}
		Collection<Mahjong> list = new ArrayList<Mahjong>();
		list.addAll(removeCard(cards, card, 1, true));
		list.addAll(removeCard(cards, card.getNext(), 1, true));
		list.addAll(removeCard(cards, card.getNext().getNext(), 1, true));
		return list;
	}

	/**
	 * 删除某张牌N张
	 * 
	 * @param cards
	 *            牌集合
	 * @param card
	 *            要删除的牌
	 * @param removeNum
	 *            要删除的数量
	 * @param mustRemoveAll
	 *            是否必须删除removeNum张 如果为false 有多少张删多少张 如果为true 牌集合中大于removeNum张牌才进行删除
	 * @return Collection<Mahjong>
	 */
	public Collection<Mahjong> removeCard(Collection<Mahjong> cards, Mahjong card, int removeNum,
			boolean mustRemoveAll) {
		if (mustRemoveAll && getCardNumFromHand(card, cards) < removeNum) {
			return Collections.emptyList();
		}
		List<Mahjong> result = new ArrayList<Mahjong>();
		Iterator<Mahjong> iteration = cards.iterator();
		while (iteration.hasNext()) {
			Mahjong c = iteration.next();
			if (c.cardEquals(card) && removeNum > 0) {
				iteration.remove();
				result.add(c);
				removeNum--;
			}
		}
		return result;
	}

	/**
	 * 删除某张牌N张
	 * 
	 * @param cards
	 *            牌集合
	 * @param card
	 *            要删除的牌
	 * @param removeNum
	 *            要删除的数量
	 * @param mustRemoveAll
	 *            是否必须删除removeNum张 如果为false 有多少张删多少张 如果为true 牌集合中大于removeNum张牌才进行删除
	 * @return Collection<Mahjong>
	 */
	public Collection<Mahjong> removeCardCheckId(Collection<Mahjong> cards, Mahjong card, int removeNum,
			boolean mustRemoveAll) {
		if (mustRemoveAll && getCardNumFromHand(card, cards) < removeNum) {
			return Collections.emptyList();
		}
		List<Mahjong> result = new ArrayList<Mahjong>();
		Iterator<Mahjong> iteration = cards.iterator();
		while (iteration.hasNext()) {
			Mahjong c = iteration.next();
			if (c.equals(card) && removeNum > 0) {
				iteration.remove();
				result.add(c);
				removeNum--;
			}
		}
		return result;
	}

	public List<Mahjong> clone(Collection<Mahjong> cards) {
		List<Mahjong> result = new ArrayList<Mahjong>();
		if (cards != null) {
			for (Mahjong card : cards) {
				result.add(card.cloneCard());
			}
		}
		return result;
	}

	public List<Mahjong> sort(Collection<Mahjong> hands) {
		List<Mahjong> resutl = new ArrayList<Mahjong>();
		if (hands != null && !hands.isEmpty()) {
			for (Mahjong card : hands) {
				resutl.add(card);
			}
		}
		return resutl;
	}

	/**
	 * 将麻将排序并且分组
	 * 
	 * @param hands
	 * @return Map<MahjongType,List<Mahjong>>
	 */
	public Map<MahjongType, List<Mahjong>> sortAndGroup(Collection<Mahjong> hands) {
		Map<MahjongType, List<Mahjong>> result = new HashMap<MahjongType, List<Mahjong>>();
		for (Mahjong card : hands) {
			List<Mahjong> list;
			MahjongType type = card.getType();
			if (!result.containsKey(type)) {
				list = new ArrayList<Mahjong>();
				result.put(type, list);
			} else {
				list = result.get(card.getType());
			}
			list.add(card);
		}

		for (MahjongType type : result.keySet()) {
			Collections.sort(result.get(type));
		}
		return result;
	}

	/**
	 * 获取某种牌的数量
	 * 
	 * @param card
	 * @param cards
	 * @return int
	 */
	public int getCardNumFromHand(IChessCard card, Collection<Mahjong> cards) {
		int result = 0;
		if (cards != null && !cards.isEmpty()) {
			for (Mahjong c : cards) {
				if (c.cardEquals(card)) {
					result++;
				}
			}
		}
		return result;
	}

	/**
	 * 牌的花色数量 散牌不算花色
	 * 
	 * @param types
	 * @return int
	 */
	public int getCardType(Collection<MahjongType> types) {
		int i = 0;
		for (MahjongType type : types) {
			if (type != MahjongType.SAN) {
				i++;
			}
		}
		return i;
	}

	/**
	 * 这张牌是否在这一副牌中
	 * 
	 * @return
	 */
	private boolean theCardIsExistInCards(Collection<Mahjong> cards, Mahjong card) {
		for (Mahjong c : cards) {
			if (c.cardEquals(card)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 删除某张牌
	 * 
	 * @param cards
	 * @param card
	 * @return Mahjong 被删除的牌
	 */
	public Mahjong removeCard(Collection<Mahjong> cards, Mahjong card) {
		if (getCardNumFromHand(card, cards) < 1) {
			return null;
		}
		Iterator<Mahjong> iteration = cards.iterator();
		while (iteration.hasNext()) {
			Mahjong c = iteration.next();
			if (c.cardEquals(card)) {
				iteration.remove();
				return c;
			}
		}
		return null;
	}

	/**
	 * 获取当前手牌的番数 如果没胡牌返回0翻
	 */
	public List<HuType> getMult(Mahjong card, MahjongGamer gamer, boolean isZimo, boolean haveGang, boolean isQiangGang) {
		// 完整的 3n+2张牌
		List<Mahjong> intactCards = MahjongCardUtil.getInstance().clone(gamer.getHandCards());
		List<Mahjong> tingCards = MahjongCardUtil.getInstance().clone(gamer.getHandCards());
		List<MahjongGroup> groups = gamer.getGroup();
		if (gamer.getHandCards().size() % 3 == 2) {// 自摸的情况从手牌里面剔除 胡的牌
			removeCard(tingCards, card);
		} else {// 将手牌加入完整的牌中
			intactCards.add(card);
		}
		if (intactCards.size() % 3 != 2) {
			return Collections.emptyList();
		}
		// 玩家没有胡牌 没有番数
		if (!checkHu(intactCards, true, gamer.getTable().getGameRules())) {
			return Collections.emptyList();
		}
		// 玩家胡牌番数情况
		List<HuType> result = new ArrayList<HuType>();
		
		boolean isRemoveJing = true;
		//德国，不用万能牌胡牌
		if(checkHu(intactCards, false, gamer.getTable().getGameRules())) {
			result.add(HuType.DEGUO);
			isRemoveJing = false;
		}
		
		int laizi = 0;
		int guiNumber = 0;
		if(isRemoveJing) {
			//移除万能牌
			Iterator<Mahjong> iterator = intactCards.iterator();
			while (iterator.hasNext()) {
				Mahjong mj = iterator.next();
				if (mj.getWannengType() != null) {
					if (mj.getWannengType() == WannengType.ShangZheng || mj.getWannengType() == WannengType.ShangFu || mj.getWannengType() == WannengType.Gui) {
						if(mj.getWannengType() == WannengType.Gui) {
							guiNumber++;
						}
						laizi++;
						iterator.remove();
					}
				}
			}
		}
		//检查四鬼胡
		if(guiNumber == 4) {
			result.add(HuType.SiGuiHu);
		}
		
		// 检查小七对
		if (checkQiDui(intactCards, laizi)) {
			result.add(HuType.XIAOQIDUI);
		}
		// 检查大七对
		if (checkDaQiDui(intactCards, groups, laizi)) {
			if(result.contains(HuType.XIAOQIDUI)) {
				result.remove(HuType.XIAOQIDUI);
			}
			result.add(HuType.DAQIDUI);
			result.add(HuType.BaoHu);
		}
		// 检查十三烂
		if (checkShiSanLan(intactCards, laizi)) {
			if (checkQiXingShiSanLan(intactCards, laizi)) {
				result.add(HuType.QIXINGSHISANLAN);
			} else {
				result.add(HuType.SHISANLAN);
			}
		}
		
		/**
		 * 上面的判断牌型如：小七对，大七对，十三烂，七星十三烂
		 */
		// 基础牌型（）
		if(result.isEmpty() || (result.size() == 1 && !isRemoveJing)) {
			result.add(HuType.PINGHU);
		}
		/**
		 * 下面的是加分牌型如：清一色，杠开，抢杠，自摸点炮
		 */
		
		// 将牌整理下
		Map<MahjongType, List<Mahjong>> sortAndGroupCards = sortAndGroup(intactCards);
		// 检查混清一色
		if (checkHunQingYiSe(sortAndGroupCards.keySet(), groups)) {
			result.add(HuType.HunYiSe);
		}
		
		// 检查纯清一色
		if (checkChunQingYiSe(sortAndGroupCards.keySet(), groups)) {
			if(checkZiYiSe(getColors(sortAndGroupCards.keySet(), groups))) {
				result.add(HuType.ZiYiSe);
			}else {
				result.add(HuType.QingYiSe);
			}
		}
		
		//自摸点炮
		if(isZimo) {
			result.add(HuType.ZIMo);
		}else {
			result.add(HuType.DIANPAO);
		}
		//杠开
		if(haveGang && card != null && card.getWannengType() != null) {
			result.add(HuType.GANGSHANGHUO);
		}
		//抢杠
		if(isQiangGang) {
			result.add(HuType.QIANGGANG);
		}
		return result;
	}

	/**
	 * 是否为混清一色 清一色+中发白
	 * 
	 * @param currentTypeNum
	 * @return boolean
	 */
	public boolean checkHunQingYiSe(Collection<MahjongType> currentTypeNum, List<MahjongGroup> groups) {
		boolean haveZhongFaBai = false;
		int typeNum = 0;
		List<MahjongType> typs = getColors(currentTypeNum, groups);
		for (MahjongType type : typs) {
			if (type == MahjongType.SAN) {
				haveZhongFaBai = true;
			} else {
				typeNum++;
			}
		}
		if (haveZhongFaBai && typeNum == 1) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * 是否为清一色 只有一种色
	 * 
	 * @param currentTypeNum
	 * @return boolean
	 */
	public boolean checkChunQingYiSe(Collection<MahjongType> currentTypeNum, List<MahjongGroup> groups) {
		return getColors(currentTypeNum, groups).size() == 1;
	}
	
	/**
	 * 检查字一色
	 * 
	 * @param currentTypeNum
	 * @return boolean
	 */
	public boolean checkZiYiSe(List<MahjongType> types) {
		if(types != null && types.size() == 1) {
			if(types.get(0) == MahjongType.SAN) {
				return true;
			}
		}
		return false;
	}
	
	public List<MahjongType> getColors(Collection<MahjongType> currentTypeNum, List<MahjongGroup> groups) {
		List<MahjongType> result = new ArrayList<MahjongType>();
		for (MahjongType type : currentTypeNum) {
			if (!result.contains(type)) {
				result.add(type);
			}
		}
		if (groups != null) {
			for (MahjongGroup group : groups) {
				if (group.getType() == GroupType.PENG || group.getType() == GroupType.GANG_A
						|| group.getType() == GroupType.GANG_B || group.getType() == GroupType.GANG_M) {
					MahjongType type = group.getMjs(0).getType();
					if (!result.contains(type)) {
						result.add(type);
					}
				}
			}
		}
		return result;
	}

	/**
	 * 获取模板牌 每种牌各一张
	 * 
	 * @return Collection<Mahjong>
	 */
	public List<Mahjong> getModelCard() {
		List<Mahjong> list = new ArrayList<Mahjong>();
//		@SuppressWarnings("unused")
//		int id = 1;
//		for (MahjongType type : MahjongType.values()) {
//			if (type == MahjongType.UNRECOGNIZED) {
//				continue;
//			}
//			if (type == MahjongType.SAN) {
//				for (int i = 1; i < 4; i++) {
//					Mahjong card = MahjongCardUtil.getMahjong(i, type);
//					list.add(card);
//					id++;
//				}
//			} else {
//				for (int i = 1; i < 10; i++) {
//					Mahjong card = MahjongCardUtil.getMahjong(i, type);
//					list.add(card);
//					id++;
//				}
//			}
//
//		}
		return list;
	}

	/**
	 * 是否有这张牌
	 * 
	 * @param card
	 * @param cards
	 * @return
	 */
	public boolean getCardNumFromHand1(IChessCard card, Collection<Mahjong> cards) {
		boolean result = true;
		if (cards != null && !cards.isEmpty()) {
			for (Mahjong c : cards) {
				if (c.equals(card)) {
					result = false;
				}
			}
		}
		return result;
	}

	/** 一副牌能胡那几张 */
	public List<Mahjong> getHuCards(Collection<Mahjong> val, MahjongRules gameRules) {
		if (val == null || val.size() % 3 != 1) {
			return Collections.emptyList();
		}
		ArrayList<Mahjong> result = new ArrayList<Mahjong>();

		List<Mahjong> modelCard = getModelCard();
		for (Mahjong card : modelCard) {
			if (checkHuNotVerificationGroup(val, card, gameRules)) {
				result.add(card);
			}
		}
		return result;
	}

	/**
	 * 检查是否胡牌 不验证手牌
	 * 
	 * @param hands
	 * @param card
	 * @param que
	 * @return boolean
	 */
	public boolean checkHuNotVerificationGroup(Collection<Mahjong> hands, Mahjong card, MahjongRules gameRules) {
		hands = clone(hands);
		hands.add(card);
		return checkHu(hands, true, gameRules);
	}

	/**
	 * 检查大七对
	 * 
	 * @param val
	 * @return
	 */
	public boolean checkDaQiDui(Collection<Mahjong> val, List<MahjongGroup> groups, int laizi) {	
		// 不能有吃牌
		if(groups != null && !groups.isEmpty()) {
			for (MahjongGroup group : groups) {
				if (group.getType() == GroupType.CHI) {
					return false;
				}
			}
		}
		// 依次删除一对牌做将，其余牌全部是碰子或杠子则为大七对
		List<Integer> cards = changeToIdMahjong(val);
		for (int i = 0; i < cards.size(); i++) {
			if (i > 0 && cards.get(i) == cards.get(i - 1)) {
				// 和上一次是同样的牌，避免重复计算
				continue;
			}
			if ((i + 1 < cards.size() && cards.get(i) == cards.get(i + 1)) || laizi > 0) {
				// 找到对子、或是用一张癞子拼出的对子
				List<Integer> puCards = new ArrayList<Integer>(cards);
				int puLaizi = laizi;
				puCards.remove(i);
				if (puCards.size() > i && puCards.get(i) == cards.get(i)) {
					puCards.remove(i);
				} else {
					puLaizi--;
				}
				// 删去对子判断剩下的牌是否全部是碰子或杠子则为大七对
				if (isDaDui(puCards, puLaizi)) {
					return true;
				}
			}
		}
		if (laizi >= 2 && isPu(cards, laizi - 2)) {
			// 两个癞子做将牌
			return true;
		}
		return false;
	}

	/**
	 * 检查是否为七对 都为对子
	 * 
	 * @param val
	 * @return boolean
	 */
	public boolean checkQiDui(Collection<Mahjong> val, int laizi) {
		if (val.size()+laizi < 14) {
			return false;
		}
		for (Mahjong card : val) {
			if (getCardNumFromHand(card, val) == 1 || getCardNumFromHand(card, val) == 3) {
				if(laizi <= 0) {
					return false;
				}
				laizi--;
			}
		}
		return true;
	}

	/**
	 * 检查是否十三烂 序数牌（条、万、饼）同一花色的两张序数牌间大于2以上加上东、南、西、北、中、发、白（风牌不一定全有，但不能重复）
	 * 
	 * @param hands
	 * @return
	 */
	public boolean checkShiSanLan(Collection<Mahjong> val, int laizi) {
		// 14张单牌
		if (val.size()+laizi != 14) {
			return false;
		}
		for (Mahjong card : val) {
			if (getCardNumFromHand(card, val) != 1) {
				return false;
			}
		}
		// 4种花色的牌都有
		Map<MahjongType, List<Mahjong>> groups = sortAndGroup(val);
		if (groups.keySet().size()+laizi < 4) {
			return false;
		}
		// 同一花色的两张序数牌间大于2
		for (List<Mahjong> list : groups.values()) {
			Collections.sort(list);
			for (Mahjong card : list) {
				if (card.getNumber() > 6) {
					continue;
				}
				if (theCardIsExistInCards(list, card.getNext())
						|| theCardIsExistInCards(list, card.getNext().getNext())) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * 检查是否十三烂 序数牌（条、万、饼）同一花色的两张序数牌间大于2以上字牌东西南北中发白各一张
	 * 
	 * @param hands
	 * @return
	 */
	public boolean checkQiXingShiSanLan(Collection<Mahjong> val, int laizi) {
		if (checkShiSanLan(val, laizi)) {
			Map<MahjongType, List<Mahjong>> groups = sortAndGroup(val);
			List<Mahjong> list = groups.get(MahjongType.SAN);
			if (list != null && list.size()+laizi >= 7) {
				return true;
			}
		}
		return false;
	}

	public Mahjong toMahjongCard(com.fanrui.proto.cmd.Mahjong mahjong) {
		return getMahjong(mahjong.getCid());
	}

	public List<Mahjong> toMahjongCard(Collection<com.fanrui.proto.cmd.Mahjong> mjs) {
		List<Mahjong> list = new ArrayList<Mahjong>();
		if (mjs != null && !mjs.isEmpty()) {
			for (com.fanrui.proto.cmd.Mahjong mj : mjs) {
				list.add(getMahjong(mj.getCid()));
			}
		}
		return list;
	}

	/**
	 * 转换牌为命令
	 */
	public List<com.fanrui.proto.cmd.Mahjong> toMahjongs(Collection<Mahjong> list) {
		List<com.fanrui.proto.cmd.Mahjong> result = new ArrayList<com.fanrui.proto.cmd.Mahjong>();
		if (list != null) {
			for (Mahjong card : list) {
				result.add(card.toCardCmd());
			}
		}
		return result;
	}

	public List<Mahjong> getEatNotPlay(List<Mahjong> cards2) {
		// TODO Auto-generated method stub
		return null;
	}

	public boolean isCanEat(Mahjong card, List<Mahjong> cards2, List<Mahjong> handCards) {
		// TODO Auto-generated method stub
		return false;
	}

	/**
	 * 癞子胡牌判断需要的麻将类型
	 */
	public List<Integer> changeToIdMahjong(Collection<Mahjong> mjs) {
		List<Integer> result = new ArrayList<Integer>();
		if (mjs != null && !mjs.isEmpty()) {
			for (Mahjong mj : mjs) {
				if (mj.getType() == MahjongType.TIAO) {
					result.add(mj.getNumber());
				} else if (mj.getType() == MahjongType.WAN) {
					result.add(mj.getNumber() + 10);
				} else if (mj.getType() == MahjongType.TONG) {
					result.add(mj.getNumber() + 20);
				} else if (mj.getType() == MahjongType.SAN) {
					result.add(mj.getNumber() * 10 + 21);
				}
			}
		}
		return result;
	}

	/**
	 * 牌集合中 4张牌的
	 * 
	 * @param val
	 * @return int
	 */
	public List<Mahjong> get4Card(Collection<Mahjong> val, Collection<MahjongGroup> groups) {
		List<Mahjong> count = new ArrayList<Mahjong>();
		for (Mahjong card : val) {
			if (getCardNumFromHand(card, val) >= 4 && getCardNumFromHand(card, count) == 0) {
				count.add(card);
			}
		}
		if (groups != null) {
			for (Mahjong card : val) {
				for (MahjongGroup mahjongGroup : groups) {
					if (mahjongGroup.getType() == GroupType.PENG && mahjongGroup.getMjsCount() > 0
							&& card.cardEquals(toMahjongCard(mahjongGroup.getMjs(0)))
							&& getCardNumFromHand(card, count) == 0) {
						count.add(card);
					}
				}
			}

		}

		return count;
	}

	/**
	 * 对一手牌的操作预期评分
	 * 
	 * @param cards
	 * @param que
	 * @param groups
	 * @return Map<Integer,LinkedList<Mahjong>>
	 */
	public int expectMarking(Collection<Mahjong> cards, Collection<MahjongGroup> groups, Mahjong card, Operation operation,
			boolean addColorScore,boolean canJiaoPlusOrMinus, MahjongRules rules) {
		cards = clone(cards);

		if (groups != null) {
			groups = new ArrayList<MahjongGroup>(groups);
		} else {
			groups = new ArrayList<MahjongGroup>();
		}

		if(operation != null && card != null){
			int removeNum = 0;
			MahjongGroup.Builder groupBuilder = MahjongGroup.newBuilder();
			if (operation == Operation.PENG) {
				removeNum = 2;
			}
			if (operation == Operation.GANG_M) {
				removeNum = 3;
			}
			if (operation == Operation.GANG_A) {
				removeNum = 4;
			}
			if (operation == Operation.GANG_B) {
				removeNum = 1;
			}
			groupBuilder.addAllMjs(toMahjongs(removeCard(cards, card, removeNum, false)));
			groupBuilder.setType(GroupType.forNumber(operation.getNumber()));
			groups.add(groupBuilder.build());
		}
		int score = getTotalSocre(marking(cards, groups, rules, false, addColorScore,canJiaoPlusOrMinus));
		// 如果能下叫多加30分
		if(canJiao(cards, rules)){
			score += 30;
		}
		// 每一个碰或杠了的牌多加分
		if(groups != null){
			for (MahjongGroup mg : groups) {
				if (checkNormalPengGang(mg.getType()) && mg.getMjsCount() > 0) {
					if (mg.getType() == GroupType.PENG) {
						score += 18;
					} else {
						score += 24;
					}

				}
			}
		}
		return score;
	}
	
	public boolean canJiao(Collection<Mahjong> cards, MahjongRules rules){
		return getRemoveAndTing(cards, rules).size() > 0;
	}
	
	public int getTotalSocre(Map<Integer, LinkedList<Mahjong>> weightSortList){
		int currentTotal = 0;
		for(int score:weightSortList.keySet()){
			currentTotal += (score * weightSortList.get(score).size());
		}
		return currentTotal;
	}
	
	/**
	 * 检查一个GroupType是否是常规碰杠
	 * 
	 * @param type
	 * @return boolean
	 */
	public boolean checkNormalPengGang(GroupType type) {
		if (GroupType.PENG == type || GroupType.GANG_A == type || GroupType.GANG_B == type
				|| GroupType.GANG_M == type) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * 给手牌的每张牌评分 评分规则：（暂定的方法 以后根据情况逐步优化）
	 * 
	 * 散牌（单张 不能组成 顺子 刻子 对子的牌 0分</br>
	 * 组成对子的牌 3分</br>
	 * 组成 卡张或边张的牌 1分</br>
	 * 连续的两张 2分 组成顺子的牌 5分 </br>
	 * 组成三张的牌 6分</br>
	 * 组成4张的牌 6分 </br>
	 * 根据胡牌情况 加减 N分
	 * 
	 * 加分：
	 * <p>
	 * 花色多的牌加分 </br>
	 * 减分 ：
	 * <p>
	 * 花色少减分 </br>
	 * 定缺的牌减 分</br>
	 * 
	 * 打出去会被人胡的牌加分 </br>
	 * 打出去会被人杠的牌加分 </br>
	 * 
	 * 各种分数可以叠加，如果一张牌即使 对子又能和其他牌组成顺子 这张牌的评分为 2 + 3 = 4； 三张和对子不会叠加 同样
	 * 4张和3张和对子也不会叠加 顺子和卡张边张不能叠加
	 * 
	 * @return Map<Integer,LinkedList<Mahjong>>
	 */
	public Map<Integer, LinkedList<Mahjong>> marking(Collection<Mahjong> cards, Collection<MahjongGroup> groups, MahjongRules rules, boolean printInfo,boolean addColorScore,boolean canJiaoPlusOrMinus) {
		Map<Integer, LinkedList<Mahjong>> result = new LinkedHashMap<>();

		Collection<Mahjong> tingList = getRemoveAndTing(cards, rules);
		if (cards != null && !cards.isEmpty()) {
			String allInfo = "得分详细情况";
			for (Mahjong card : cards) {
				String info = "[" + card.toString() + "得分情况 -- ";
				List<ScoreGroupType> scoreGroups = markingCard(card, cards);

				int score = 0;
				for (ScoreGroupType sgt : scoreGroups) {
					score += sgt.getScore();
				}
				info += "组合【" + scoreGroups.toString() + "】得分:" + score;
				int colorScore = addColorScore?getTypeSize(cards, card.getType()):0;
				if(card.getType() == MahjongType.SAN){//散牌不加花色得分
					colorScore = 0;
				}
				// 加上 花色得分
				score += colorScore;
				// 碰杠牌得分
				int groupScore = getGroupScore(card.getType(), groups);
				score += groupScore;

				//能听牌
				int tingScore = getTingScore(card, tingList);
				if(canJiaoPlusOrMinus){
					score += tingScore;
				}else{
					score += Math.abs(tingScore);
				}
				
				
				info += " 听牌得分：" + tingScore;
				info += " 花色得分：" + colorScore;
				info += " 碰和杠得分：" + groupScore;
				info += "总得分：" + score + "]";
				allInfo += info;
				LinkedList<Mahjong> list = result.get(score);
				if (list == null) {
					list = new LinkedList<Mahjong>();
					result.put(score, list);
				}
				list.add(card);
			}
			if (printInfo) {
				Stdout.GET().info(allInfo);
			}
		}
		return result;
	}
	
	/**
	 * 根据整手牌给某张牌评分
	 * 
	 * @return int
	 */
	public List<ScoreGroupType> markingCard(Mahjong card, Collection<Mahjong> cards) {
		List<ScoreGroupType> result = new LinkedList<ScoreGroupType>();

		// 有几张相同的牌？
		ScoreGroupType cardNumType = getSocreByCardNum(getCardNumFromHand(card, cards));
		if (cardNumType != null) {
			result.add(cardNumType);
		}
		
		// 字牌 加分
		if(card.getType() == MahjongType.SAN){
			result.add(ScoreGroupType.ZiPai);
		}
		// 是否能组成顺子
		if ((getCardNumFromHand(card.getNext(), cards) >= 1 && getCardNumFromHand(card.getNextN(2), cards) >= 1)
				|| (getCardNumFromHand(card.getNextN(-1), cards) >= 1
						&& getCardNumFromHand(card.getNextN(-2), cards) >= 1)
				|| (getCardNumFromHand(card.getNext(), cards) >= 1
						&& getCardNumFromHand(card.getNextN(-1), cards) >= 1)) {
			result.add(ScoreGroupType.Order3);
		} else if (getCardNumFromHand(card.getNext(), cards) >= 1
				|| getCardNumFromHand(card.getNextN(-1), cards) >= 1) {// 不能组成顺子的情况下
																		// 看能否组成
																		// 连续的两张
																		// 这里要注意边张
			result.add(ScoreGroupType.Order2);
		} else if (getCardNumFromHand(card.getNextN(2), cards) >= 1
				|| getCardNumFromHand(card.getNextN(-2), cards) >= 1) {// 是否能组成卡张
			result.add(ScoreGroupType.UnOrder2);
		} else {
			result.add(ScoreGroupType.SanPai);
		}
		return result;

	}
	
	/**
	 * 移除后可以下叫的牌
	 * 
	 * @param card
	 * @param cards
	 * @param que
	 * @return Set<Mahjong>
	 */
	public Set<Mahjong> getRemoveAndTing(Collection<Mahjong> cards, MahjongRules rules) {
		Set<Mahjong> result = new HashSet<Mahjong>();
		if (cards != null) {
			for (Mahjong card : cards) {
				Collection<Mahjong> clonCards = clone(cards);
				removeCard(clonCards, card, 1, false);
				if (isJiao(clonCards, rules)) {
					result.add(card);
				}
			}
		}
		return result;
	}
	
	/**
	 * 是否已经下叫
	 * 
	 * @param val
	 * @param que
	 * @return boolean
	 */
	public boolean isJiao(Collection<Mahjong> val, MahjongRules rules) {
		List<Mahjong> huCards = getHuCards(val, rules);
		return !huCards.isEmpty();
	}
	
	/**
	 * 获取类型的数量
	 * 
	 * @param cards
	 * @param type
	 * @return int
	 */
	public int getTypeSize(Collection<Mahjong> cards, MahjongType type) {
		Map<MahjongType, List<Mahjong>> sortAndGroup = sortAndGroup(cards);
		if (sortAndGroup == null || sortAndGroup.isEmpty() || !sortAndGroup.containsKey(type)) {
			return 0;
		} else {
			return sortAndGroup.get(type).size();
		}
	}
	
	/**
	 * 碰杠组合得分
	 * 
	 * @param type
	 * @param groups
	 * @return int
	 */
	public int getGroupScore(MahjongType type, Collection<MahjongGroup> groups) {
		int result = 0;
		if (groups != null && !groups.isEmpty()) {
			for (MahjongGroup mg : groups) {
				if (checkNormalPengGang(mg.getType()) && mg.getMjsCount() > 0 && mg.getMjs(0).getType() == type) {
					if (mg.getType() == GroupType.PENG) {
						result += 3;
					} else {
						result += 4;
					}

				}
			}
		}
		return result;
	}
	
	/**
	 * 牌的数量对应分数
	 * 
	 * @param cardNum
	 * @return int
	 */
	private ScoreGroupType getSocreByCardNum(int cardNum) {
		switch (cardNum) {
		case 2:
			return ScoreGroupType.Twin;
		case 3:
			return ScoreGroupType.Three;
		case 4:
			return ScoreGroupType.Four;
		}
		return null;
	}
	
	public int getTingScore(Mahjong card, Collection<Mahjong> tingList) {
		// 如果是打出后可以下叫的牌减分
		if (getCardNumFromHand(card, tingList) > 0) {
			return -15;
		} else {
			return 0;
		}
	}
	
	private enum ScoreGroupType {
		UnOrder2(1), // 不连续的两张 卡张或边张
		Order3(5), // 连续的三张
		Order2(2), // 连续的两张
		Four(6), // 四张相同的
		Three(6), // 三张相同的
		Twin(3), // 两张相同的
		ZiPai(4),// 字牌加分
		SanPai(0); // 散牌
		private int score;

		private ScoreGroupType(int score) {
			this.score = score;
		}

		public int getScore() {
			return score;
		}

	}

}
